package com.le.tester.javaThink.fourth.concurrency;


import com.le.tester.javaThink.fourth.enumerated.menu.Course;
import com.le.tester.javaThink.fourth.enumerated.menu.Food;
import com.le.tester.javaThink.fourth.enumerated.menu.Meal2;
import com.le.tester.javaThink.fourth.net.mindview.util.Print;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/**
 * createTime：2022/1/19 16:31
 * description：饭店仿真
 */

class Order {
    private static int counter = 0;

    private final int id = counter++;

    private final RestaurantCustomer customer;

    private final RestaurantWaitPerson waitPerson;

    private final Food food;

    public Order(RestaurantCustomer customer, RestaurantWaitPerson waitPerson, Food food) {
        this.customer = customer;
        this.waitPerson = waitPerson;
        this.food = food;
    }

    public Food item() {
        return food;
    }

    public RestaurantCustomer getCustomer() {
        return customer;
    }

    public RestaurantWaitPerson getRestaurantWaitPerson() {
        return waitPerson;
    }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", customer=" + customer +
                ", waitPerson=" + waitPerson +
                ", food=" + food +
                '}';
    }
}

class Plate {
    private final Order order;

    private final Food food;

    public Plate(Order order, Food food) {
        this.order = order;
        this.food = food;
    }

    public Order getOrder() {
        return order;
    }

    public Food getFood() {
        return food;
    }

    @Override
    public String toString() {
        return "Plate{" +
                "order=" + order +
                ", food=" + food +
                '}';
    }

}

class RestaurantCustomer implements Runnable {

    private static int counter = 0;

    private final int id = counter++;

    private final RestaurantWaitPerson waitPerson;

    private SynchronousQueue<Plate> placeSetting = new SynchronousQueue<>();

    public RestaurantCustomer(RestaurantWaitPerson waitPerson) {
        this.waitPerson = waitPerson;
    }

    public void deliver(Plate p) throws InterruptedException {
        //放进synchronizedQueue
        placeSetting.put(p);
    }

    @Override
    public void run() {
        for (Course course : Course.values()) {
            Food food = course.randomSelection();
            try {
                waitPerson.placeOrder(this, food);
                Print.print(this + " eating " + placeSetting.take());
            } catch (InterruptedException e) {
                Print.print(this + " waiting for " + course + " interrupted");
                break;
            }
        }
        Print.print(this + " finished meal,leaving");
    }

    public String toString() {
        return "Customer " + id + " ";
    }
}

class RestaurantWaitPerson implements Runnable {
    private static int counter = 0;

    private final int id = counter++;

    private final RestaurantSelf restaurant;

    BlockingQueue<Plate> filledOrders = new LinkedBlockingQueue<>();

    public RestaurantWaitPerson(RestaurantSelf restaurant) {
        this.restaurant = restaurant;
    }

    public void placeOrder(RestaurantCustomer customer, Food food) {
        try {
            restaurant.orders.put(new Order(customer, this, food));
        } catch (InterruptedException e) {
            Print.print(this + " placeOrder interrupted");
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Plate plate = filledOrders.take();
                Print.print(this + " received " + plate + " delivering to" + plate.getOrder().getCustomer());
                plate.getOrder().getCustomer().deliver(plate);
            }
        } catch (InterruptedException e) {
            Print.print(this + " interrupted");
        }
        Print.print(this + " off duty");
    }

    public String toString() {
        return "WaitPerson " + id + " ";
    }
}

class RestaurantSelf implements Runnable {
    private List<RestaurantWaitPerson> waitPersons = new ArrayList<>();

    private List<RestaurantChef> chefs = new ArrayList<>();

    private ExecutorService exec;

    private static Random random = new Random(47);

    BlockingQueue<Order> orders = new LinkedBlockingQueue<>();

    public RestaurantSelf(ExecutorService exec, int nWaitPersons, int nChefs) {
        this.exec = exec;

        for (int i = 0; i < nWaitPersons; i++) {
            RestaurantWaitPerson waitPerson = new RestaurantWaitPerson(this);
            waitPersons.add(waitPerson);
            exec.execute(waitPerson);
        }

        for (int i = 0; i < nChefs; i++) {
            RestaurantChef chef = new RestaurantChef(this);
            chefs.add(chef);
            exec.execute(chef);
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                RestaurantWaitPerson wp = waitPersons.get(random.nextInt(waitPersons.size()));

                RestaurantCustomer customer = new RestaurantCustomer(wp);
                exec.execute(customer);
                TimeUnit.MILLISECONDS.sleep(100);
            }
        } catch (InterruptedException e) {
            Print.print("restaurant interrupted");
        }
        Print.print("restaurant closing");
    }
}

class RestaurantChef implements Runnable {

    private static int counter = 0;

    private final int id = counter++;

    private final RestaurantSelf restaurant;

    private static Random random = new Random(47);

    public RestaurantChef(RestaurantSelf restaurant) {
        this.restaurant = restaurant;
    }

    @Override
    public void run() {

        try {
            while (!Thread.interrupted()) {
                Order order = restaurant.orders.take();
                Food food = order.item();
                TimeUnit.MILLISECONDS.sleep(random.nextInt(500));
                Plate plate = new Plate(order, food);
                order.getRestaurantWaitPerson().filledOrders.put(plate);
            }
        } catch (InterruptedException e) {
            Print.print(this + " interrupted");
        }
        Print.print(this + " off duty");
    }

    public String toString() {
        return "Chef " + id + " ";
    }
}

public class RestaurantWithQueues {
    public static void main(String[] args) throws IOException, InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        RestaurantSelf restaurantSelf = new RestaurantSelf(exec, 5, 2);
        exec.execute(restaurantSelf);
        if (args.length > 0)
            TimeUnit.SECONDS.sleep(new Integer(args[0]));
        else {
            Print.print("Press enter to quit");
            System.in.read();
        }
        exec.shutdownNow();
    }

}
